﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
namespace PHPUniTester
{
    class Program
    {

        //a nice little bit of code i got from Rick strahl's weblog :3
        //http://www.west-wind.com/weblog/posts/197245.aspx
        public static Encoding GetFileEncoding(string srcFile)
        {

            // *** Use Default of Encoding.Default (Ansi CodePage)
            Encoding enc = Encoding.Default;

            // *** Detect byte order mark if any - otherwise assume default
            byte[] buffer = new byte[5];
            FileStream file = new FileStream(srcFile, FileMode.Open);
            file.Read(buffer, 0, 5);
            file.Close();

            if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf)
                enc = Encoding.UTF8;
            else if (buffer[0] == 0xfe && buffer[1] == 0xff)
                enc = Encoding.Unicode;
            else if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff)
                enc = Encoding.UTF32;
            else if (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76)
                enc = Encoding.UTF7;
            return enc;
        }


        public static List<string> dangerousFunctions(string content)
        {
            List<string> result = new List<string>();

            if (content.Contains(" ord("))
            {
                result.Add("ord");
            }


            if (content.Contains(" str_ireplace("))
            {
                result.Add("str_ireplace");
            }

            if (content.Contains(" str_pad("))
            {
                result.Add("str_pad");
            }


            if (content.Contains(" str_split("))
            {
                result.Add("str_split");
            }


            if (content.Contains(" strcasecmp("))
            {
                result.Add("strcasecmp");
            }


            if (content.Contains(" strcspn("))
            {
                result.Add("strcspn");
            }

            if (content.Contains(" stristr("))
            {
                result.Add("stristr");
            }

            if (content.Contains(" strlen("))
            {
                result.Add("strlen");
            }

            if (content.Contains(" strpos("))
            {
                result.Add("strpos");
            }

            if (content.Contains(" strrpos("))
            {
                result.Add("strrpos");
            }

            if (content.Contains(" strrev("))
            {
                result.Add("strrev");
            }

            if (content.Contains(" strspn("))
            {
                result.Add("strspn");
            }



            if (content.Contains(" strtolower("))
            {
                result.Add("strtolower");
            }

            if (content.Contains(" strtoupper("))
            {
                result.Add("strtoupper");
            }


            if (content.Contains(" substr_replace("))
            {
                result.Add("substr_replace");
            }

            if (content.Contains(" ltrim("))
            {
                result.Add("ltrim (only unsafe with second argument $charlist");
            }


            if (content.Contains(" rtrim("))
            {
                result.Add("rtrim (only unsafe with second argument $charlist");
            }

            if (content.Contains(" ucfirst("))
            {
                result.Add("ucfirst");
            }

            if (content.Contains(" ucwords("))
            {
                result.Add("ucwords");
            }

            return result;
        }

        static void Main(string[] args)
        {

            List<PageTestResult> result = new List<PageTestResult>();


            if (args.Length > 0)
            {


                if (args[0].Equals("-?"))
                {
                    Console.WriteLine(@"PhpUniTester <path> to scan that directory
PhpUniTester <path> -i to iterate trough subfolders as well
PhpUniTester <path> -i -oe same as above only errors
");

                }
                else
                {
                    string path = args[0];

                    DirectoryInfo directory = new DirectoryInfo(path);


                    foreach (FileInfo info in directory.GetFiles("*.php"))
                    {
                        //info.FullName;
                        Encoding encoding = GetFileEncoding(info.FullName);
                        PageTestResult pageresult = new PageTestResult();

                        pageresult.Path = info.FullName;
                        pageresult.FileEncoding = encoding.EncodingName;

                        string content = File.ReadAllText(info.FullName, encoding);

                        Regex regex = new Regex(@"<meta[^>]*charset=([^""]*)""");
                        MatchCollection matches = regex.Matches(content);
                        pageresult.PageEncoding = matches[0].Groups[1].Value;

                        pageresult.DangerousFunctions = dangerousFunctions(content);

                        result.Add(pageresult);


                        if (args.Length >= 2)
                        {
                            if (args[1].Equals("-i"))
                            {

                                foreach (DirectoryInfo directory2 in directory.GetDirectories())
                                {

                                    result = IterativeFunction(result, directory2);

                                }
                            }
                        }

                    }






                    if (args.Length >= 3)
                    {
                        if (args[2].Equals("-oe"))
                        {
                            Console.WriteLine(formatResultOnlyError(result));
                        }
                    }
                    else
                    Console.WriteLine(formatResult(result));
            


                }

            }

            else
            {
                Console.WriteLine(@"PhpUniTester <path> to scan that directory
PhpUniTester <path> -i to iterate trough subfolders as well
PhpUniTester <path> -i -oe same as above only errors
");
        
            }

            Console.ReadLine();

        }



        public static string formatResultOnlyError(List<PageTestResult> data)
        {
            string result = "";
            foreach (PageTestResult d in data)
            {
                //this if statment is a bit wobbly but it seams valid for at least utf8....
                string file = d.FileEncoding.ToLower();
                string page = d.PageEncoding.ToLower();
                bool noDangerousFunctions = d.DangerousFunctions.Count != 0;
                bool pageAndfileMatch = file.Contains(page);

                if (noDangerousFunctions|| !pageAndfileMatch)
                {


                result += string.Format(@"------------------------------------
Path:{0}
PageEncoding:{1}
FileEncoding:{2}
Dangerous functions:", d.Path, d.PageEncoding, d.FileEncoding);

                foreach (string s in d.DangerousFunctions)
                    result += string.Format("{0}|", s);
                result += @"
------------------------------------";

            }


            }

            return result;
        
        }
     



        public static string formatResult(List<PageTestResult> data)
        {
            string result = "";
            foreach (PageTestResult d in data)
            {

                result += string.Format(@"------------------------------------
Path:{0}
PageEncoding:{1}
FileEncoding:{2}
Dangerous functions:", d.Path, d.PageEncoding, d.FileEncoding);

                foreach (string s in d.DangerousFunctions)
                    result += string.Format("{0}|", s);
                result += @"
------------------------------------";

            }

            return result;
        
        }
        public static List<PageTestResult> IterativeFunction(List<PageTestResult> result, DirectoryInfo directory)
        {


            foreach (FileInfo info in directory.GetFiles("*.php"))
            {
                //info.FullName;
                Encoding encoding = GetFileEncoding(info.FullName);
                PageTestResult pageresult = new PageTestResult();

                pageresult.Path = info.FullName;
                pageresult.FileEncoding = encoding.EncodingName;

                string content = File.ReadAllText(info.FullName, encoding);

                Regex regex = new Regex(@"<meta[^>]*charset=([^""]*)""");
                MatchCollection matches = regex.Matches(content);
                pageresult.PageEncoding = matches[0].Groups[1].Value;

                pageresult.DangerousFunctions = dangerousFunctions(content);

                result.Add(pageresult);
                foreach (DirectoryInfo directory2 in directory.GetDirectories())
                {
                    result = IterativeFunction(result, directory2);
                }


            }



            return result;
        }


    }




}
